/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package lolbatfsm.implementation.gui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.AbstractButton;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import lolbatfsm.system.AsynchronousTransition;
import lolbatfsm.system.FSM;
import lolbatfsm.system.SynchronousTransition;

/**
 * This class is a form which let the user choose the FSMs and their
 * transitions that have to iterate.<br>
 * To do this it uses a JScrollPanel with a GridBagLayout,
 * so it doesn't matter how many transitions you pass the class,<br>
 * you'll be able to scroll them.<br>
 * For each FSM there is a ButtonGroup which contains one JRadioButton
 * for each transition.<br>
 * When clicking on Ok button the transitions's indexes, selected with their
 * JradioButtons, are added in the bidimensional array
 * selectedTransitionIndexes.<br>
 * The Simulator will use this array to know which transitions were choosen
 * by the user.<br>
 * There is one button: clicking on Ok you'll start the
 * simulation.
 *
 * @author Fabio Lola <fadan87@gmail.com>
 */
public class SimulatorGUI extends javax.swing.JDialog implements ActionListener, ItemListener {

    Map<JRadioButton, Object> radioTransitions;
    Map<FSM, ButtonGroup> transitionGroups;
    private final ArrayList<AsynchronousTransition> selectableAsyncTransitions;
    private final ArrayList<? extends Collection<SynchronousTransition>> selectableSyncTransitions;
    private final boolean allowNullSelection;
    private Integer[][] selectedTransitionIndexes;
    private final MainForm mainForm;

    public static SimulatorGUI createAndShow(ArrayList<AsynchronousTransition> selectableAsyncTransitions, ArrayList<? extends Collection<SynchronousTransition>> selectableSyncTransitions, boolean allowNullSelection, MainForm mainForm) {
        SimulatorGUI simulatorGUI = new SimulatorGUI(selectableAsyncTransitions, selectableSyncTransitions, allowNullSelection, mainForm);
        simulatorGUI.setLocation(200, 100);
        simulatorGUI.setModal(true);
        simulatorGUI.setVisible(true);
        return simulatorGUI;
    }

    public static SimulatorGUI createAndShowTest(ArrayList<AsynchronousTransition> selectableAsyncTransitions, ArrayList<? extends Collection<SynchronousTransition>> selectableSyncTransitions, boolean allowNullSelection, MainForm mainForm) {
        SimulatorGUI simulatorGUI = new SimulatorGUI(selectableAsyncTransitions, selectableSyncTransitions, allowNullSelection, mainForm);
        simulatorGUI.setLocation(200, 100);
        simulatorGUI.setVisible(true);
        return simulatorGUI;
    }
    
    /**
     * @param selectableAsyncTransitions Where Simulator puts enabled
     * Asynchronous tranistions
     * @param selectableSyncTransitions Where Simulator puts enabled
     * Synchronous tranistions
     * @param allowNullSelection True if there are pending transitions,
     * and the user isn't forced to select at least one transition
     */
    private SimulatorGUI(ArrayList<AsynchronousTransition> selectableAsyncTransitions, ArrayList<? extends Collection<SynchronousTransition>> selectableSyncTransitions, boolean allowNullSelection, MainForm mainForm) {
        initComponents();
        this.mainForm = mainForm;
        this.allowNullSelection = allowNullSelection;
        this.selectableAsyncTransitions = selectableAsyncTransitions;
        this.selectableSyncTransitions = selectableSyncTransitions;
        radioTransitions = new HashMap<JRadioButton, Object>();
        transitionGroups = new HashMap<FSM, ButtonGroup>();
        for (AsynchronousTransition transition : selectableAsyncTransitions) {
            if (!transitionGroups.containsKey(transition.getOwnerFSM())) {
                ButtonGroup fsmGroup = new ButtonGroup();
                JRadioButton anyButton = new JRadioButton("Nothing selected");
                fsmGroup.add(anyButton);
                anyButton.addActionListener(this);
                anyButton.addItemListener(this);
                transitionGroups.put(transition.getOwnerFSM(), fsmGroup);
                radioTransitions.put(anyButton, null);
            }
            JRadioButton button = new JRadioButton(transition.toString());
            button.addActionListener(this);
            button.addItemListener(this);
            transitionGroups.get(transition.getOwnerFSM()).add(button);
            radioTransitions.put(button, transition);
        }
        for (Collection<SynchronousTransition> transitions : selectableSyncTransitions) {
            for (SynchronousTransition transition : transitions) {
                if (!transitionGroups.containsKey(transition.getOwnerFSM())) {
                    JRadioButton anyButton = new JRadioButton("Nothing selected");
                    ButtonGroup fsmGroup = new ButtonGroup();
                    fsmGroup.add(anyButton);
                    anyButton.addActionListener(this);
                    anyButton.addItemListener(this);
                    transitionGroups.put(transition.getOwnerFSM(), fsmGroup);
                    radioTransitions.put(anyButton, null);
                }
                JRadioButton button = new JRadioButton(transition.toString());
                button.addActionListener(this);
                button.addItemListener(this);
                transitionGroups.get(transition.getOwnerFSM()).add(button);
                radioTransitions.put(button, transitions);
            }
        }
        GridBagConstraints c = new GridBagConstraints();
        GridBagLayout gridBagLayout = new GridBagLayout();
        panel.setLayout(gridBagLayout);
        c.gridx = 0;
        c.gridy = 0;
        c.anchor = GridBagConstraints.NORTHWEST;
        c.weightx = 0.5;
        c.weighty = 0.1;
        c.insets = new Insets(5, 5, 5, 0);
        for (Entry<FSM, ButtonGroup> entry : transitionGroups.entrySet()) {
            panel.add(new JLabel(entry.getKey().getName()), c);
            c.gridy++;
            ButtonGroup buttonGroup = entry.getValue();
            Enumeration<AbstractButton> buttons = buttonGroup.getElements();
            while (buttons.hasMoreElements()) {
                panel.add(buttons.nextElement(), c);
                c.gridy++;
            }
        }
        c.gridy++;
        c.weighty = 1.0;
        panel.add(Box.createVerticalGlue(), c);
        okButton.setEnabled(allowNullSelection);
        this.pack();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        panel = new javax.swing.JPanel();
        okButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setResizable(false);

        jScrollPane1.setBorder(null);

        panel.setAutoscrolls(true);
        panel.setLayout(new java.awt.GridLayout(1, 0));
        jScrollPane1.setViewportView(panel);

        okButton.setText("Ok");
        okButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                okButtonMouseClicked(evt);
            }
        });

        cancelButton.setText("Stop Simulation");
        cancelButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                cancelButtonMouseClicked(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 494, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(cancelButton)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 341, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(cancelButton)
                    .addComponent(okButton))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void okButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_okButtonMouseClicked
        if (okButton.isEnabled()) {
            selectedTransitionIndexes = new Integer[2][];
            ArrayList<Integer> asynchronousTransitions = new ArrayList<Integer>();
            ArrayList<Integer> synchronousTransitions = new ArrayList<Integer>();
            Integer count = 0;
            if (panel instanceof JPanel) {
                for (int i = 0; i < panel.getComponentCount(); i++) {
                    if (panel.getComponent(i) instanceof JRadioButton) {
                        JRadioButton button = (JRadioButton) panel.getComponent(i);
                        if (button.isSelected()) {
                            Object transition = radioTransitions.get(button);
                            if (transition != null) {
                                if (transition instanceof AsynchronousTransition) {
                                    asynchronousTransitions.add(selectableAsyncTransitions.indexOf(transition));
                                    count++;
                                } else {
                                    Integer index = selectableSyncTransitions.indexOf(transition);
                                    if (!synchronousTransitions.contains(index)) {
                                        synchronousTransitions.add(index);
                                        count++;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            selectedTransitionIndexes[0] = new Integer[asynchronousTransitions.size()];
            selectedTransitionIndexes[1] = new Integer[synchronousTransitions.size()];
            asynchronousTransitions.toArray(selectedTransitionIndexes[0]);
            synchronousTransitions.toArray(selectedTransitionIndexes[1]);

            if (!allowNullSelection && count == 0) {
                okButton.setEnabled(false);
                JOptionPane.showMessageDialog(null, "Select at least one transition", "WARNING", JOptionPane.WARNING_MESSAGE);
            } else {
                mainForm.ended = false;
                this.setVisible(false);
            }
        }
    }//GEN-LAST:event_okButtonMouseClicked

    private void cancelButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cancelButtonMouseClicked
        selectedTransitionIndexes = null;
        mainForm.ended = true;
        this.setVisible(false);
    }//GEN-LAST:event_cancelButtonMouseClicked

    /**This method checks that if a synchronous transition is selected,
     * all the synchronous transitions enabled and with the same equivalence
     * class are enabled too.
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        JRadioButton button = (JRadioButton) e.getSource();
        if (!button.getText().equals("Nothing selected")) {
            okButton.setEnabled(true);
        }
        Object transitions = radioTransitions.get(button);
        if (transitions instanceof Collection<?>) {
            for (Entry<JRadioButton, Object> entry : radioTransitions.entrySet()) {
                if (button != entry.getKey() && entry.getValue() instanceof Collection<?>) {
                    if (transitions.equals(entry.getValue())) {
                        entry.getKey().setSelected(true);
                    }
                }
            }
        }
    }

    public Integer[][] getIterationResult() {
        return selectedTransitionIndexes;
    }

    /**
     * This method checks that if one synchornous transition has been deselected
     * by user, then all the enabled synchronous transitions with the same
     * equivalence class are deselected.
     */
    @Override
    public void itemStateChanged(ItemEvent e) {
        JRadioButton button = (JRadioButton) e.getSource();
        Object transitions = radioTransitions.get(button);
        if ((e.getStateChange() == ItemEvent.DESELECTED) && transitions instanceof Collection<?>) {
            for (Entry<JRadioButton, Object> entry : radioTransitions.entrySet()) {
                if (button != entry.getKey() && entry.getValue() instanceof Collection<?>) {
                    //scorro tutti i gruppi di transizioni reciprocamente sincrone
                    if (transitions.equals(entry.getValue())) {
                        //il radio button si riferisce ad una transition reciprocamente sincorna a quella di button
                        for (SynchronousTransition transition : (Collection<SynchronousTransition>) transitions) {
                            Enumeration<AbstractButton> buttons = transitionGroups.get(transition.getOwnerFSM()).getElements();
                            while (buttons.hasMoreElements()) {
                                JRadioButton next = (JRadioButton) buttons.nextElement();
                                if (radioTransitions.get(next) == null) {
                                    next.setSelected(true);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public void testOkButtonClick(){
        okButton.setEnabled(true);
        okButtonMouseClicked(new MouseEvent(cancelButton, 0, 0, 0, 0, 0, 1, false));
    }

    public void testCancelButtonClick(){
        cancelButtonMouseClicked(new MouseEvent(cancelButton, 0, 0, 0, 0, 0, 1, false));
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton cancelButton;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton okButton;
    private javax.swing.JPanel panel;
    // End of variables declaration//GEN-END:variables
}
